/**
 * 甘特图
 * jq.gantt-1.0.3
 * @author Brant Liu
 * @email lbf1988@qq.com
 * @date 2020/7/7
 * @version 1.0.0
 */
(function (global, factory) {
    "use strict";
    factory(global);
}(typeof window !== "undefined" ? window : this, function (window) {
    "use strict";

    var $win = $(window),
        $doc = $(document);

    var ID_ATTR = 'id', ID_PREFIX = 'gantt-',

        /**
         * 自定列
         * */
        PROPERTY_AUTO_KEY = '*',

        /**
         * 内置列属性
         * */
        PROPERTY_ID = 'id', PROPERTY_PARENT = 'parent', PROPERTY_TEXT = 'text',
        PROPERTY_START_DATE = 'startDate', PROPERTY_END_DATE = 'endDate', PROPERTY_DURATION = 'duration',
        PROPERTY_FINAL_START_DATE = 'finalStartDate', PROPERTY_FINAL_END_DATE = 'finalEndDate',
        PROPERTY_FINAL_DURATION = 'finalDuration',
        PROPERTY_OWNER = 'owner',

        /**
         * selector
         * */
        ELEM_GRID = '.gantt-grid', ELEM_TASK = '.gantt-task', ELEM_RESIZER = '.gantt-resizer', ELEM_CONTENT = '.gantt-content',
        ELEM_HEADER = '.gantt-header', ELEM_DATA = '.gantt-data', ELEM_ROW = '.gantt-row', ELEM_CELL = '.gantt-cell',
        ELEM_X_SCROLL = '.gantt-x-scroll', ELEM_Y_SCROLL = '.gantt-y-scroll',
        ELEM_LOADER = '.gantt-loader';

    var config = {
        index: 20200707000,
        instance: [],
        column: {
            text: {field: PROPERTY_TEXT, text: 'Task Name', width: PROPERTY_AUTO_KEY},
            startDate: {field: PROPERTY_START_DATE, text: 'Start Date', width: 100},
            duration: {field: PROPERTY_DURATION, text: 'Duration', width: 80}
        }
    };

    /**
     * 工具
     * */
    var tool = {
        toObject: function (arr, key) {
            key = key || 'field';
            var obj = {};
            if (!$.isArray(arr)) {
                arr = [arr];
            }
            $.each(arr, function (i, item) {
                if (item.hasOwnProperty(key)) {
                    obj[item.field] = item;
                }
            });
            return obj;
        },
        toArray: function (obj) {
            var arr = [];
            for (var key in obj) {
                arr.push(obj[key]);
            }
            return arr;
        },
        getWidth: function ($parent) {
            if (!$parent[0]) {
                return $win.width();
            }
            /**
             * 2为容器的左右边框
             * 1为容器中间分割线
             * */
            var width = $parent.outerWidth() - 2 - 1;
            return width;
        },
        next: function(date, number) {
            number = number || 1;
            var now = date || new Date(), arr = [now];
            for (var i = 1; i < number; i++) {
                var day = new Date(now);
                day.setDate(day.getDate() + i);
                arr.push(day);
            }
            return arr;
        },
        header: function(callback){
            var self = this;
            return [
                '<div class="gantt-content">',
                    '<div class="gantt-header">',
                        callback ? callback.call(self) : '',
                    '</div>',
                    '<div class="gantt-data"></div>',
                '</div>',
                '<div class="gantt-x-scroll"></div>'
            ].join('');
        },
        template: function(){
            var self = this, options = self.options,
                num = self.taskWidth / options.minWidthTask,
                taskCellNum = num + (num * options.minWidthTask >= self.taskWidth ? 0 : 1);
            return [
                '<div class="gantt-layout" style="min-width:'+ (self.gridWidth + self.gridWidth/2) +'px; height:'+(options.height)+'px;">',
                    '<div class="gantt-wrapper">',
                        '<div class="gantt-grid" style="'+ (self.gridWidth && 'width:'+ self.gridWidth +'px;') +'">',
                            tool.header.call(self, function(){
                                var cellTpl = ['<div class="gantt-row">'];
                                self.each(function(i, column){
                                    cellTpl.push('<div class="gantt-cell"><span>' + column[PROPERTY_TEXT] + '</span></div>');
                                });
                                cellTpl.push('</div>');
                                return cellTpl.join('');
                            }),
                        '</div>',
                        '<div class="gantt-resizer">',
                            options.resize ? '<div class="gantt-content"></div>' : '',
                        '</div>',
                        '<div class="gantt-task" style="'+ (self.taskWidth && 'width:'+ self.taskWidth +'px;') +'">',
                            tool.header.call(self, function(){
                                var range = tool.next(options.date || new Date(), taskCellNum), taskTpl = ['<div class="gantt-row">'];
                                $.each(range, function(i, date){
                                    taskTpl.push('<div class="gantt-cell"><span>' + date.getDate() + '</span></div>');
                                });
                                taskTpl.push('</div>');
                                return taskTpl.join('');
                            }),
                        '</div>',
                        '<div class="gantt-y-scroll"></div>',
                        '<div class="gantt-loader"><div class="gantt-loader-circle"></div></div>',
                        (function(){
                            var styleTpl = ['<style type="text/css">'];
                            $.each(self.style, function(i, item){
                                styleTpl.push(item);
                            });
                            styleTpl.push('</style>');
                            return styleTpl.join('');
                        }()),
                    '</div>',
                '</div>'
            ].join('');
        }
    };

    /**
     * Gantt构造器
     * */
    var Gantt = function (selector, options) {
        this.index = ++config.index;
        this.options = $.extend({}, this.options, options);
        this.$container = $(selector);
        this.init();
    };

    /**
     * 原型
     * */
    Gantt.fn = Gantt.prototype;

    /**
     * 配置
     * */
    Gantt.fn.options = {
        data: null,
        width: null,
        height: 500,
        /**
         * 甘特图左侧表格计算规则
         * > 1 直接体现为左侧宽度
         * = 0 根据列计算宽度
         * < 1 按照父容器百分比计算宽度
         * */
        gridWidth: 0,
        /**
         * 表格最小宽度
         * */
        minWidth: 100,
        /**
         * 表格描述列默认宽度
         * */
        minWidthTaskDesc: 200,
        /**
         * 甘特图日历最小宽度
         * */
        minWidthTask: 32,
        /**
         * 左右容器拖动开关
         * */
        resize: true,
        page: 1,
        limit: 20,
        success: function () {
        }
    };

    /**
     * 初始化
     * */
    Gantt.fn.init = function () {
        var self = this, options = self.options, $container = self.$container;

        options.id = options.id || $container.attr(ID_ATTR) || (ID_PREFIX + self.index);
        $container.attr(ID_ATTR) || $container.attr(ID_ATTR, options.id);
        $container && $container[0] && $container.empty();

        self.initColumns()
            .initLayoutProperty();
        self.initLayout();

        console.log(self);

        //记录实例，便于onresize事件处理
        config.instance.push(self);
    };

    /**
     * 初始化表格配置项
     * */
    Gantt.fn.initColumns = function () {
        var self = this, options = self.options,
            columns = options.columns || [];

        var columnOps = tool.toArray($.extend(true, config.column, tool.toObject(columns)));
        var columnFinal = columns.length > 0 ? [] : columnOps;
        $.each(columns, function (i, item) {
            $.each(columnOps, function (i2, item2) {
                if (item2.field == item.field) {
                    columnFinal.push(item2);
                }
            });
        });

        $.each(columnFinal, function (i, col) {
            if (!col.width && col.width != PROPERTY_AUTO_KEY) {
                col.width = options.minWidth;
            }
            col.resize = col.resize || false;
        });

        self.columns = [columnFinal];

        return self;
    };

    /**
     * 初始化属性
     * */
    Gantt.fn.initLayoutProperty = function () {
        var self = this, options = self.options,
            gridWidthCal = options.gridWidth, isPercent = gridWidthCal < 1 && gridWidthCal > 0,
            containerWidth = options.width || tool.getWidth(self.$container),
            stylePrefix = ['#', options.id, ' ', '.gantt-grid', ' ', '.gantt-cell'].join(''),
            style = [];

        var sumWidth = self.getGridWidthSum();
        var width = self.gridWidth = isPercent ? parseInt(containerWidth * gridWidthCal) : (gridWidthCal ? (sumWidth > gridWidthCal ? gridWidthCal : sumWidth) : sumWidth);
        self.taskWidth = containerWidth - width;

        var columnUseWidth = 0;
        self.each(function (i, item) {
            if (!isNaN(item.width)) {
                columnUseWidth += item.width;
            }
        });
        var autoWidth = width - columnUseWidth;
        self.each(function (i, item) {
            var width = item.cellWidth ? item.cellWidth : item.width;
            if (isPercent && width == PROPERTY_AUTO_KEY) {
                width = autoWidth;
            }
            style.push(stylePrefix + ':nth-child(' + (i + 1) + '){width: ' + width + 'px}');
        });
        self.style = style;

        return self;
    };

    /**
     * 更新属性
     * */
    Gantt.fn.updateLayoutProperty = function () {
        var self = this, options = self.options,
            containerWidth = options.width || tool.getWidth(self.$container);
        self.taskWidth = containerWidth -self.gridWidth;

        self.$task.css('width', self.taskWidth);

        console.log(self.$taskHeader.find(ELEM_ROW).eq(0).outerWidth());
        self.loader(false);
    };

    /**
     * 初始化容器
     * */
    Gantt.fn.initLayout = function(){
        var self = this, $container = self.$container;

        var $layout = self.$layout = $(tool.template.call(self));
        delete self.style;

        $container.append($layout);

        self.$grid = $container.find(ELEM_GRID);
        self.$gridHeader = self.$grid.find(ELEM_HEADER);
        self.$gridData = self.$grid.find(ELEM_DATA);

        self.$task = $container.find(ELEM_TASK);
        self.$taskHeader = self.$task.find(ELEM_HEADER);
        self.$taskData = self.$task.find(ELEM_DATA);

        self.$loader = $container.find(ELEM_LOADER);

        self.$scrollbarY = $container.find(ELEM_Y_SCROLL);

        self.updateLayoutProperty();

        return self;
    };

    /**
     * 获取已知列宽度
     * */
    Gantt.fn.getGridWidthSum = function () {
        var self = this, options = self.options, minWidth = options.minWidthTaskDesc, width = 0;

        self.each(function (i, item) {
            if (item.width && item.width > 0) {
                width += item.width;
            } else {
                width += item.cellWidth = minWidth;
            }
        });

        return width || 500;
    };

    /**
     * 加载状态
     * */
    Gantt.fn.loader = function (enabled) {
        var self = this;
        self.$loader[enabled ? 'fadeIn':'fadeOut']('slow');
        return self;
    };

    /**
     * 异步处理
     * */
    Gantt.fn.run = function (callback, timer) {
        var self = this;
        timer = timer || 50;
        setTimeout(function () {
            callback.call(self);
        }, timer);
        return self;
    };

    /**
     * 遍历表格列
     * */
    Gantt.fn.each = function (callback) {
        var self = this, columns = self.columns[0];
        $.each(columns, function (i, item) {
            callback && callback.apply(self, [i, item]);
        });
    };

    /**
     * 外部访问接口
     * @param {String} selector 选择器
     * @param {Object} options  配置项
     * */
    Gantt.init = function (selector, options) {
        return new Gantt(selector, options);
    };

    window.Gantt = Gantt;
}));